package com.xingzhe.pengji.security.support;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;

import com.danga.MemCached.MemCachedClient;
import com.xingzhe.pengji.security.domain.Authorite;
import com.xingzhe.pengji.security.domain.Resource;
import com.xingzhe.pengji.security.services.AuthoriteService;
import com.xingzhe.pengji.security.services.ResourceService;
import com.xingzhe.pengji.security.tool.AntUrlPathMatcher;
import com.xingzhe.pengji.security.tool.UrlMatcher;

/**
 * 资源源数据定义，将所有的资源和权限对应关系建立起来，即定义某一资源可以被哪些角色访问
 * 该过滤器的主要作用就是通过spring著名的IoC生成securityMetadataSource。
 * securityMetadataSource相当于本包中自定义的MyInvocationSecurityMetadataSourceService。
 * 该MyInvocationSecurityMetadataSourceService的作用提从数据库提取权限和资源，装配到HashMap中，
 * 供Spring Security使用，用于权限校验。
 */
public class MyInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource
{

    @Autowired
    private AuthoriteService<Authorite> authoriteService;

    @Autowired
    private ResourceService<Resource> resourceService;

    @Autowired
    private MemCachedClient memCachedClient;

    private UrlMatcher urlMatcher = new AntUrlPathMatcher();

    private static Map<String, Collection<ConfigAttribute>> resourceMap = null;

    /**
     * 1、自定义方法，这个类放入到Spring容器后， 指定init为初始化方法，从数据库中读取资源
     * 2、加载所有资源与权限的关系 
     */
    @SuppressWarnings("unused")
    private void loadResourceDefine()
    {

        resourceMap = (Map<String, Collection<ConfigAttribute>>) memCachedClient.get("SPRING_SECURITY_RESOURCE_MAP");

        if (null == resourceMap)
        {
            resourceMap = new HashMap<String, Collection<ConfigAttribute>>();

            List<String> listAuthoritiesName = authoriteService.getAllAuthoritieName();
            /*
             * 应当是资源为key， 权限为value。 资源通常为url， 权限就是那些以ROLE_为前缀的角色。 一个资源可以由多个权限来访问。 sparta
             */
            for (String authoriteName : listAuthoritiesName)
            {
                ConfigAttribute ca = new SecurityConfig(authoriteName);
                List<String> listResourcesUrl = resourceService.getResourcesByAuthoritieName(authoriteName);
                for (String url : listResourcesUrl)
                {
                    /*
                     * 判断资源文件和权限的对应关系，如果已经存在相关的资源url，则要通过该url为key提取出权限集合，将权限增加到权限集合中。
                     */
                    if (resourceMap.containsKey(url))
                    {
                        Collection<ConfigAttribute> value = resourceMap.get(url);
                        value.add(ca);
                        resourceMap.put(url, value);
                        memCachedClient.add("SPRING_SECURITY_RESOURCE_MAP", resourceMap);
                    }
                    else
                    {
                        Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
                        atts.add(ca);
                        resourceMap.put(url, atts);
                        memCachedClient.add("SPRING_SECURITY_RESOURCE_MAP", resourceMap);
                    }
                }
            }
        }
        else
        {
            LoggerFactory.getLogger(MyInvocationSecurityMetadataSourceService.class).debug("调用缓存来完成加载全部的权限和资源的关系");
        }
    }

    // 根据URL，找到相关的权限配置
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes()
    {
        Set<ConfigAttribute> allAttributes = new HashSet<ConfigAttribute>();
        for (Map.Entry<String, Collection<ConfigAttribute>> entry : resourceMap.entrySet())
        {
            allAttributes.addAll(entry.getValue());
        }
        return allAttributes;
    }

    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException
    {
        // object 是一个URL，被用户请求的url。
        String url = ((FilterInvocation) object).getRequestUrl();
        int firstQuestionMarkIndex = url.indexOf("?");
        if (firstQuestionMarkIndex != -1)
        {
            url = url.substring(0, firstQuestionMarkIndex);
        }
        Iterator<String> ite = resourceMap.keySet().iterator();
        while (ite.hasNext())
        {
            String resURL = ite.next();
            if (urlMatcher.pathMatchesUrl(url, resURL))
            {
                return resourceMap.get(resURL);
            }
        }
        return null;
    }

    @Override
    public boolean supports(Class<?> arg0)
    {
        return true;
    }

}
